#!/usr/bin/python
import subprocess
import sys
import re
import os
from glob import glob1
import shutil
import signal
import optparse

def main():
	
	programs = ["neroAacEnc","ffmpeg", "makemkvcon", "aften", "MP4Box", "mkvextract", "x264", "mediainfo"]
	needed = list()
	for program in programs:
		if not which(program):
			needed.append(program)
	merge = True
	if len(needed) == 1 and "mediainfo" in needed:
		merge = False
	elif len(needed) > 0:
		raise Exception("You do not have " + " ".join(needed))

	(opts, args) = parse_options()	
	bd = BluRay2MKV(folder=opts.output)
	mkvs = bd.start()
	for audio_track, title in mkvs:
		mkvcomp(audio_track, title)
	
	dest_filename = os.path.join(bd.dest, bd.title + ".m4v")
	test(dest_filename)

	if len(mkvs) < 1:
		raise Exception("No video streams found on " + bd.title)
	mp4s = list()
	for audio_track, title in mkvs:
		mkm = MKV2MP4(title.path, tmp=os.path.join('/tmp', bd.title), frames=title.total_frames, seconds=title.length_int, channels=title.streams[int(audio_track)].channels)
		mp4s.append(mkm.start())
		

#	if len(mp4s) > 1 and merge:
#		mm = MergeMP4s("23.976", dest)
#		for mp4 in mp4s:
#			mm.update(mp4)
#		mm.start()
	if len(mp4s) == 1:
		os.rename(mp4s[0], dest_filename)
	
		
	print " [o] All Done!"	


def mkvcomp(audio_track, title):
	sys.stdout.write(" [+] Remuxing MKV for ffmpeg support...")
	sys.stdout.flush()
	video_track = "1"
	basename = os.path.basename(title.path)
	dirname = os.path.dirname(title.path)
	new_path = os.path.join(dirname, os.path.splitext(basename)[0] + ".new.mkv")
	cmd = ["mkvmerge", "-o", new_path, "-d", video_track, "-a", str(int(float(audio_track)) + 1), "-S", title.path]	
	fd = open(os.devnull, 'a')
	mkv = None
	s = Spinny()
	try:
		mkv = subprocess.Popen(cmd, stdout=fd, stderr=subprocess.STDOUT)
		while mkv.poll() is None:
			s.p()
		if mkv.poll() > 0:
			raise Exception("mkvextract crashed")
	except SystemExit:
		s.finish("failed")
		try:
			mkv.kill()
		except Exception:
			pass
		raise SystemExit
	except Exception, e:
		s.finish("failed")
		try:
			mkv.kill()
		except Exception:
			pass
		raise e
	finally:
		try:
			fd.close()
		except Exception:
			pass
	try:
		os.remove(title.path)
		while True:
			try:
				os.rename(new_path, title.path)
				break
			except Exception:
				continue
	except Exception:
		title.path = new_path
	
	
	s.finish("success")	
	return 

def which(program):
    def is_exe(fpath):
        return os.path.exists(fpath) and os.access(fpath, os.X_OK)

    fpath, fname = os.path.split(program)
    if fpath:
        if is_exe(program):
            return True
    else:
        for path in os.environ["PATH"].split(os.pathsep):
            exe_file = os.path.join(path, program)
            if is_exe(exe_file):
                return True

    return False

def test(path):
	if os.path.isfile(path) or os.path.isdir(path):
		print " [-] The file " + os.path.basename(path) + " already exists"
		while True:
			input = raw_input("\t Would you like to overwrite[y/n]: ")
			if str(input).strip().lower() == "y":
				if os.path.isdir(path):
					try:
						os.rmdir(path)
						break
					except Exception:
						print " [-] " + os.path.basename(path) + " is a non-empty directory"
						while True:
							del input
							input = raw_input("\t Woud you like to recursively delete[y/n]: ")
							if str(input).strip().lower() == "y":
								try:
									shutil.rmtree(path)
									break
								except Exception:
									raise Exception("Could not delete directory contents")
							elif str(input).strip().lower() == "n":
								raise SystemExit
				else:
					try:
						os.remove(path)
						break
					except Exception:
						raise Exception("Could not delete " + os.path.basename(path))
			elif str(input).strip().lower() == "n":
				raise SystemExit
	return


class BluRay2MKV:
	def __init__(self, folder=None, dest=None):
		self.title_audio_tracks = list()
		self.title = 'title'
		self.folder = folder
		if dest:
			self.dest = os.path.abspath(dest)
		else:
			self.dest = os.getcwd()

	def start(self):
		echo(' [+] Getting drive information...')
		try:
			drive_list = self.get_drive_info()
		except SystemExit:
			necho('failed')
			raise SystemExit
		except Exception, e:
			necho('failed')
			raise e
		necho("success")
		echo(' [+] Finding Blu-Ray/HD-DVD drives...')
		try:
			hd_drives = self.check_bd_drive(drive_list)
		except SystemExit:
			necho('failed')
			raise SystemExit
		except Exception, e:
			necho('failed')
			raise e
		necho('success')
		if len(hd_drives) > 1:
			drive_index = self.input_get_drive(hd_drives)
		elif len(hd_drives) < 1:
			raise Exception('Could not find any blu-ray/hd-dvd drives')
		else:
			drive_index = 0
		#Get information about the selected drive
		drive = hd_drives[drive_index]
		#Set the title of media
		if self.folder:
			self.title = self.folder
		else:
			self.title = drive.disc_name
		#Set new dest folder
		new_dest_path = os.path.join(self.dest, self.title)
		self.dest = new_dest_path
		
		echo(' [+] Getting title information from ' + drive.disc_name + '...')

		try:
			self.disc = self.get_disc_info(drive)
		except SystemExit:
			necho('failed')
			raise SystemExit
		except Exception, e:
			necho('failed')
			raise e

		necho('success')
		#Find Top Titles
		top_titles = self.disc.top_titles(drive.type)	
		#Check to make sure new dest exists
		if not os.path.isdir(self.dest):
			try:
				os.mkdir(self.dest)	
			except Exception:
				raise Exception('Could not make destination folder' + os.path.basename(self.dest))
		#CHECK FOR ENOUGH SPACE
		(space, needed_space) = self.enough_space(top_titles, self.dest)
		if not space:	
			raise Exception("Not enough space. You need " + str(needed_space) + " GB")

		audio_tracks = self.pick_audio_track(top_titles)		
			
		print " [+] Found " + str(len(top_titles)) + " feature title(s)"
		for title in top_titles:
			print_info = list()
			if len(title.title.strip()) > 0:
				print_info.append("Title: " + title.title.strip())
			else:
				print_info.append("Filename: " + title.filename.strip())
			if len(title.size.strip()) > 0:
				print_info.append("Size: " + title.size.strip())
			echo(" [+] Making mkv from " + " ".join(print_info))
			self.make_mkv(title, hd_drives[drive_index], self.dest)

		if len(top_titles) != len(audio_tracks):
			raise("Error audio track information")
		return zip(audio_tracks, top_titles)

	def pick_audio_track(self, titles, lang=['eng']):
		lang_chart = {'eng':'English', 'fra':'French', 'spa':'Spanish', 'por':'Portuguese'}
		try:
			temp = lang
			for item in temp:
				lange.append(lange_chart[item])
		except Exception:
			pass
					
		title_tracks = list()
		for title in titles:
			tracks = list()
			for stream in title.streams:
				if stream.stream_type.lower().strip() == 'audio':
					if stream.language.lower() in lang or stream.language_expanded.lower() in lang:
						tracks.append(stream)
			if len(tracks) < 1:
				raise Exception("No audio tracks found in " + " or ".join(lang))
			title_tracks.append(tracks)
		title_good_tracks = list()
		for title in title_tracks:
			good_tracks = list()
			index = 1
			for track in tracks:
				if "a_dts" in track.codec.lower():
					if "dts-hd" not in  track.codec_formatted.lower() and "dts-hd" not in track.codec_formatted_expanded.lower():
						if index == 1:
							good_tracks.insert(index-1, track.track.strip())
							index = 2
						elif index > 1:
							good_tracks.insert(index-1, track.track.strip())
							index = 3
				elif track.codec.lower() == "a_ac3":
					good_tracks.append(track.track.strip())
				elif track.codec.lower() == "a_truehd":
					good_tracks.insert(index-1, track.track.strip())
					index = 2
			if len(good_tracks) < 1:
				print " [-] No good audio tracks found..this may result in bad audio"
				title_good_tracks.append("1")
			else:	
				title_good_tracks.append(good_tracks[0])
		return title_good_tracks
		
	def enough_space(self, titles, dest):
		if len(titles) < 1:
			raise Exception("Error processing space")
		stat = os.statvfs(dest)
		space_mb = float(stat.f_bavail * stat.f_frsize) / 1024 / 1024

		needed_space = 0.0
		needed_mb_re = re.compile('.*?([0-9\.]*).*?mb.*?', re.I)
		needed_gb_re = re.compile('.*?([0-9\.]*).*?gb.*?', re.I)
		for title in titles:
			if title.size.strip() == "":
				raise Exception("Size on title " + title.title + " could not be found")
			needed_mb = needed_mb_re.search(title.size.strip().lower())
			needed_gb = needed_gb_re.search(title.size.strip().lower())
			if needed_mb:
				current_space = needed_mb.group(1) 
				try:
					needed_space += float(current_space)
					del current_space
				except Exception:
					raise Exception("Error in size data")
					
			elif needed_gb:
				current_space = needed_gb.group(1)
				try:
					needed_space += float(current_space) * 1024
					del current_space
				except Exception:
					raise Exception("Error in size data")
			else:
				raise Exception("Size data unknown")
		needed_space += 7*1024

		if space_mb < needed_space:
			return (False, round(needed_space/1024, 1))
		else:
			return (True, round(needed_space/1024, 1))
			
		

	def input_get_drive(self, drives):
		for x in xrange(len(drives)):
			print "\t [" + str(x) + "] " + drives[x].disc_name
		selection = 0
		while True:
			selection = raw_input("\t Selection: ")	
			if selection.isdigit() and int(selection) >= 0 and int(selection) < len(drives):
				return int(selection) 


	def make_mkv(self, title, drive, dest):
		import time
		import progressbar
		if not os.path.isdir(title.path):
				os.mkdir(title.path)
		else:
			shutil.rmtree(title.path)
			os.mkdir(title.path)
		cmd = ["makemkvcon", "-r", '--messages=-null', "mkv", "disc:" + drive.index, title.index, title.path]	
		make = None
		try:		
			make = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
			sys.stdout.write('\n')
			sys.stdout.flush()
			max_size = int(round(title.size_float*1024*1024*1024))
			pbar = progressbar.ProgressBar(widgets=[progressbar.FileTransferSpeed(), progressbar.Bar('>'), progressbar.Percentage(), progressbar.ReverseBar('<'), progressbar.ETA()], maxval=max_size).start()
			filename = title.path
			time.sleep(1)
			filename = None
			while make.poll() is None:
				try:
					if filename:
						file_size = int(self.get_file_size(filename))
						pbar.update(file_size)
						time.sleep(.1)
					else:
						files = glob1(title.path, "*.mkv")
						if len(files) ==  1:
							filename = os.path.join(title.path, files[0])
				except Exception:
					pass
			if make.poll() > 0:
				raise Exception("Could not make mkv")	
		except SystemExit:
			raise SystemExit
		except Exception, e:
			raise e
		finally:
			try:
				make.kill()
			except Exception:
				pass
			pbar.finish()

		if not os.path.isdir(title.path):
			raise Exception('MakeMKV did not output the correct file')

		files = glob1(title.path, "*.mkv")
		if len(files) != 1:
			raise Exception('MakeMKV did not output the correct file')

		else:
			rename = os.path.join(dest, files[0])
			if os.path.isfile(rename):
				os.remove(rename)
			elif os.path.isdir(rename):
				os.rmdir(rename)
			os.rename(os.path.join(title.path, files[0]), rename)
			os.rmdir(title.path)
			title.path = rename
		
			

	def get_file_size(self, path):
		if not os.path.exists(path):	
			return 0.0
		size_bytes = 0.0
		try:
			size_bytes = os.path.getsize(path)
		except Exception:
			return 0.0
		return round(size_bytes)
		
	def timeout(self, signum, frame):
		raise Exception


	def get_disc_info(self,drive):
		disc = DiscObject()
		cmd = ["makemkvcon", "-r", '--messages=-stdout', '--progress=-stderr', "info", "disc:" + drive.index]
		make = None
		started = False
		try:
			make = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, stdin=subprocess.PIPE)
			info_lines = list()
			info = make.stdout.readline()
			while make.poll() is None and len(info) > 0:
				try:
					if started:
						signal.signal(signal.SIGALRM, self.timeout)
						signal.alarm(20)
					info = make.stdout.readline()
					if "TCOUNT" in info:
						started = True
					if info != None and len(info) > 0:
						info_lines.append(info)
					signal.alarm(0)
				except Exception:
					break
		except SystemExit:
			try:
				make.kill()
			except Exception:
				pass
			raise SystemExit

		if make.poll() > 0:
			raise Exception('makemkv crashed')

		for line in info_lines:
			tcount_re = re.search('^TCOUNT:(.*)', line)
			if tcount_re != None:
				disc.title_count = int(str(tcount_re.group(1)).strip())
				break
			

		tinfo = list()
		sinfo = list()
		current_title = 0
		for line in info_lines:
			if line[0:2] != "SI" and line[0:2] != "TI":
				continue

			title_number = line[6:].split(",")[0]

			if title_number.strip() != str(current_title):
				title = self.get_title_info(tinfo, sinfo)
				title.index = str(current_title)
				title.path = os.path.join(self.dest, "title" + str(current_title))
				disc.titles.append(title)
				tinfo[:] = []
				sinfo[:] = []	
				current_title = current_title + 1
				
			tinfo_type_re = re.search('^TINFO:(.*)', line)
			sinfo_type_re = re.search('^SINFO:(.*)', line)
			if tinfo_type_re != None:
				tinfo.append(tinfo_type_re.group(1))
				
			elif sinfo_type_re != None:
				sinfo.append(sinfo_type_re.group(1))	
		
		title = self.get_title_info(tinfo, sinfo)		
		title.index = str(current_title)
		title.path = os.path.join(self.dest, "title" + str(current_title))
		del sinfo
		del tinfo
		disc.titles.append(title)
		return disc
			
				
	def get_title_info(self,tinfo, sinfo):
		title = TitleObject()
		for line in tinfo:
			data_list = line.split(",")	
			try:
				if data_list[1] == "2":
					title.title = data_list[3].strip('"')
				elif data_list[1] == "8":
					title.short_id = data_list[3].strip('"')
				elif data_list[1] == "9":
					title.length = data_list[3].strip('"')
					title.get_length_int()
					title.get_total_frames()
				elif data_list[1] == "10":
					title.size = data_list[3].strip('"')	
					size_mb_re = re.search('(.*)mb.*', title.size.lower())
					size_gb_re = re.search('(.*)gb.*', title.size.lower())
					if size_mb_re != None:
						mb_string = size_mb_re.group(1)
						mb_string = mb_string.strip()	
						title.size_float = float(mb_string)/1024
					elif size_gb_re != None:
						gb_string = size_gb_re.group(1)
						gb_string = gb_string.strip()
						title.size_float = float(gb_string)
					
				elif data_list[1] == "11":
					title.long_id = data_list[3].strip('"')
				elif data_list[1] == "16":
					title.filename = data_list[3].strip('"')
				else:
					continue
					
			except Exception:
			#	raise Exception("Could not get title info")
				pass
		stream_info = list()
		current_stream = 0
		for line in sinfo:
			stream_number = int(line.split(",")[1])
			if stream_number != current_stream:
				stream = self.get_stream_info(stream_info)
				title.streams.append(stream)
				stream_info[:] = []
				current_stream = current_stream + 1

			stream_info.append(line)	
		stream = self.get_stream_info(stream_info)
		title.streams.append(stream)
		stream_info[:] = []
		return title

	def get_stream_info(self, sinfo):
		stream = StreamObject()	
		try:
			stream.track = sinfo[0].split(",")[1].strip()
		except Exception:
			raise Exception("Could not get stream track info")
		for line in sinfo:
			stream_list = line.split(",")
			try:
				if stream_list[2] == "1":
					stream.stream_type = stream_list[4].strip('"')	
				elif stream_list[2] == "3":
					stream.language = stream_list[4].strip('"')	
				elif stream_list[2] == "4":
					stream.language_expanded = stream_list[4].strip('"')	
				elif stream_list[2] == "5":
					stream.codec = stream_list[4].strip('"')	
				elif stream_list[2] == "6":
					stream.codec_formatted = stream_list[4].strip('"')	
				elif stream_list[2] == "7":
					stream.codec_formatted_expanded = stream_list[4].strip('"')	
				elif stream_list[2] == "13":
					stream.bitrate = stream_list[4].strip('"')	
				elif stream_list[2] == "17":
					stream.sample_rate = stream_list[4].strip('"')	
				elif stream_list[2] == "19":
					stream.resolution = stream_list[4].strip('"')	
				elif stream_list[2] == "20":
					stream.aspect_ratio = stream_list[4].strip('"')	
				elif stream_list[2] == "21":
					stream.framerate = stream_list[4].strip('"')	
				elif stream_list[2] == "14":
					stream.channels = stream_list[4].strip('"')
				else:
					continue
			except Exception:
				#raise Exception("Could not get stream info")
				pass

		return stream

		


	def check_bd_drive(self, drive_list):
		hd_drives = list()
		for drive in drive_list:
			if len(drive.disc_name) < 1:
				continue
			drive_name = drive.drive_name
			drive_results_re = re.search('.*bd.*', drive_name.lower())
			if drive_results_re != None:
				drive.type = "bd"
				hd_drives.append(drive)
			del drive_results_re
			drive_results_re = re.search('.*hd.+dvd.*', drive_name.lower())
			if drive_results_re != None:
				drive.type = "hddvd"
				hd_drives.append(drive)

		return hd_drives
			


	def get_drive_info(self):
		drive_list = list()
		cmd = ["makemkvcon", "-r", "info", "disc:9999"]
		p = None
		try:
			p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE)
			return_code = p.wait()
			if return_code > 0:
				raise Exception('makemkvcon crashed')	
		except Exception, e :
			raise e
		except SystemExit:
			raise SystemExit
		finally:
			try:
				p.kill()
			except Exception:
				pass
		
		infolines = p.stdout.readlines()
		for line in infolines:
			information_re = re.search('^DRV:(.*)', line)
			if information_re != None:
				information_string = information_re.group(1)
				information_list = str(information_string).split(",")
				try:
					if str(information_list[2]) == "0":
						continue
				except Exception, e:
					raise e
					

				drive = DriveObject()
				try:
					drive.index = str(information_list[0])
					drive.drive_name = str(information_list[4]).strip('"')
					drive.disc_name = str(information_list[5]).strip('"') 
				except Exception, e:
					raise e				

				drive_list.append(drive)
		return drive_list

def echo(string):
	sys.stdout.write(string)
	sys.stdout.flush()

def necho(string):
	sys.stdout.write(string + '\n')
	sys.stdout.flush()
			
		
class DiscObject:
	def __init__(self):
		self.titles = list()
		self.title_count = 0

	def top_titles(self, drive_type):
		thresh = 0
		if drive_type == "bd":
			thresh = 17
		elif drive_type == "hddvd":
			thresh = 8
		else:
			raise Exception("Unknown disc type")

		titles = self.find_large_titles(thresh)
		if len(titles) < 1:
			title = self.choose(self.titles)
			return [title]
		if drive_type == "bd" and len(titles) == 1:
			return titles
		elif drive_type == "bd":
			title = None
			title = self.choose(titles)
			return [title]
			#if len(temp) == 1:
			#	return temp	
			#else:
			#	title = self.choose(titles)
			#	temp = list()
			#	temp.append(title)
			#	return temp
		else:
			largest = self.largest_title(titles)
			if largest is None:
				title = self.choose(self.titles)
				temp = list()
				temp.append(title)
				return temp
			temp = list()
			temp.extend(titles)
			temp.remove(largest)
			matching_titles = self.titles_by_name(largest, largest)
			if len(matching_titles) == 1:
				matching_titles.append(largest)
				return matching_titles
			else:
				title1 = self.choose(titles)
				temp = list()
				temp.append(title1)
				titles.remove(title1)
				title2 = self.choose(titles)
				temp.append(title2)
				return temp

	def choose(self, titles):
		for title, count in zip(titles, xrange(len(titles))):
			print "\t[" + str(count) + "] " + title.index + "  " + title.length + "  " + title.size
		while True:
			input = raw_input("\tSelection: ")
			if input.isdigit() and int(input.strip()) >= 0 and int(input.strip()) < len(titles):
				return titles[int(input.strip())] 
	def find_smallest_size(self, titles):
		smallestnum = 100
		smallest = None
		for title in titles:
			size = title.size_float
			if size < smallestnum:
				smallestnum = size
				smallest = title

	def find_closest_size(self, titles, av=30):
		lowest_diff = 50.0
		final_title = None
		for title in titles:
			diff = title.size_float - float(av)
			if diff < lowest_diff and diff > -10:
				lowest_diff = diff
				final_title = title
		return final_title
		
	

	def find_large_titles(self, thresh):
		top_titles = list()
		size_thresh = thresh
		for title in self.titles:
			size = title.size_float
			if size > size_thresh:
				top_titles.append(title)

		return top_titles

	def largest_title(self, titles):
		top_size = 0.0
		top_title = None
		for title in titles:
			if title.size_float > top_size:
				top_size = title.size_float
				top_title = title	
		return top_title


	def titles_by_name(self, top_title, titles):
		matchin_titles = list()
		top_title_name = top_title.title	
		top_title_filename, ext = os.path.splitext(top_title.filename)
		top_title_sections = top_title_name.split("_")	
		top_title_filename_sections = top_title_filename.split("_")
		for sect in top_title_sections:
			sect_re = re.compile('.*' + re.escape(str(sect).strip().lower()) + '.*')
			for title in titles:
				(filename, ext) = os.path.splitext(title.filename)
				if ext == ".mpls":
					continue
				title_name = title.title
				if title_name == top_title_name:
					continue
				sect_match_re = sect_re.search(title_name.lower())
				if sect_match_re != None and title not in matching_titles:
					matching_titles.append(title)
		del sect
		del title

		for sect in top_title_filename_sections:
			if sect.isdigit():
				continue
			sect_filename_re = re.compile('.*' + re.escape(str(sect).strip().lower()) + '.*')
			for title in titles:
				(filename, ext) = os.path.splitext(title.filename)
				if ext == ".mpls":
					continue
				title_filename = title.filename
				if title_filename == top_title_filename + ext:
					continue
				sect_match_filename_re = sect_filename_re.search(title_filename.lower())
				if sect_match_filename_re != None and title not in matching_titles:
					matching_titles.append(title)

		return matching_titles

			

		
				
				


class TitleObject:
	def __init__(self):	
		self.title = ""
		self.short_id = ""
		self.length = ""	
		self.length_int = 0
		self.total_frames = 0
		self.size = ""	
		self.size_float = 0.0
		self.filename = ""
		self.long_id = ""
		self.index = ""
		self.streams = list()

	def get_length_int(self):
		if len(self.length) > 0:
			try:
				sects = self.length.split(':')	
				hours = sects[0]
				minutes = sects[1]
				seconds = sects[2]
				total = (int(hours)*60*60) + (int(minutes) * 60) + int(seconds)
				total = int(round(total))
				self.length_int = total
			except Exception,e :
				self.length_int = 0

	def get_total_frames(self, fps=23.976):
		try:
			fps = float(fps)
			total = self.length_int * fps	
			total = int(round(total))
			self.total_frames = total
		except Exception:
			self.total_frames = 0

class StreamObject:
	def __init__(self):
		self.track = ""
		self.stream_type = ""
		self.language = ""
		self.language_expanded = ""
		self.codec = ""
		self.codec_formatted = ""
		self.codec_formatted_expanded = ""
		self.bitrate = ""	
		self.sample_rate = ""
		self.resolution = ""
		self.aspect_ratio = ""
		self.framerate = ""
		self.channels = "2"




class DriveObject:
	def __init__(self):
		self.index = ""
		self.drive_name = ""
		self.disc_name = ""
		self.type = None
		
class MergeMP4s:
	def __init__(self, fps, dest):
		self.fps = fps
		self.dest = dest
		self.files = list()
	def update(self, file):
		self.files.append(file)
			
	def start(self):
		files_info = list()	
		for file in self.files:
			streams_info = self.get_file_info(os.path.abspath(file))
			files_info.append(streams_info)
			del streams_info
		if len(self.files) != len(files_info):
			raise Exception("Could not retrieve all stream info")

		for x in xrange(len(files_info)):		
			if x+1 < len(files_info):
				self.compare(files_info[x], files_info[x+1])

		names_and_info = self.alter_stream_list(self.files, files_info)
		final_streams = list()	
		delete_list = list()
		for stream_type in names_and_info:
			cat_files = list()
			stream_type.reverse()
			for stream, file in stream_type:
				new_filename = self.extract(str(int(stream.track) +1), os.path.abspath(file))
				cat_files.append(new_filename.strip())
				delete_list.append(new_filename.strip())
			
			final_stream = self.cat(cat_files)
			final_streams.append(final_stream)
			delete_list.append(final_stream)

		mp4box = list()
		for file in final_streams:
			mp4box.append("-add")
			mp4box.append(file)
		mp4box.append("-fps")
		mp4box.append(self.fps)

		self.mux_streams(mp4box, self.dest)
		
		self.delete.append(delete_list)

		return


	def delete(self,file_list):
		for file in file_list:
			try:
				os.remove(file)
			except Exception:
				pass
		return



	def cat(self, file_list):
		(dirname, basename) = os.path.split(file_list[0])
		(file_basename, ext) = os.path.splitext(basename)
		file_name = os.path.join(dirname, file_basename + ".new" + ext)
		cat_list = list()
		for file in file_list:
			cat_list.append(file)
		cmd = ["cat"]
		cmd.extend(cat_list)
		fh = open(file_name, "w")
		null = open('/dev/null', 'w')
		p = None
		try:
			p = subprocess.Popen(cmd, stdout=fh, stderr=null)
			return_code = p.wait()
			fh.close()
			null.close()
			if return_code > 0:
				raise Exception("Could not output " + file_name)
		except SystemExit:
			try:
				p.kill()
			except Exception:
				pass
			raise SystemExit
		if not os.path.isfile(file_name):
			raise Exception("Error outputing file")
		return os.path.abspath(file_name)
			
			
		

	def alter_stream_list(self, filenames, files_info):
			stream_list = list()
			try:
				for stream in xrange(len(files_info[0])):
					type_list = list()
					for file in xrange(len(files_info)):
						type_list.append((files_info[file][stream], filenames[file]))
					stream_list.append(type_list)
			except Exception:
				raise Exception("No file info")

			return stream_list
				
					
				

	def mux_streams(self, mp4box, output):
		cmd = ["MP4Box"]
		cmd.extend(mp4box)
		cmd.append(output)
		p = None
		try:
			p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
			return_code = p.wait()
			if return_code > 0:
				raise Exception("Error muxing video")
		except SystemExit:
			try:
				p.kill()
			except Exception:
				pass
			raise SystemExit
		return

			
						
				

	def extract(self, track, file):
		cmd = ["MP4Box", "-raw", track, file]	
		p = None
		try:
			p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
			return_code = p.wait()
			if return_code > 0:
				raise Exception("Error retrieving track " + track + " from " + os.path.basename(file))
		except SystemExit:
			try:
				p.kill()
			except Exception:
				pass
			raise SystemExit

		(file_root, ext) = os.path.splitext(os.path.basename(file))
		new_file_root = file_root + "_track" + track
		file_list = glob1(os.path.dirname(file), new_file_root + "*")
		new_file = ""
		for line in file_list:
			file_search_re = re.search('.*' + re.escape(new_file_root.strip()) + '.*', line.strip())	
			if file_search_re:
				new_file = os.path.join(os.path.dirname(file), line)
				break
		if len(new_file) < 1:
			raise Exception("Error finding new file")

		new_file = os.path.abspath(new_file)
		return new_file	


	def compare(self, stream_list1, stream_list2):
		if len(stream_list1) != len(stream_list2):
			raise Exception("Streams are not the same!")

		for x, y in zip(stream_list1, stream_list2):
			if len(x) != len(y):
				raise Exception("Streams are not the same!")
			for a,b in zip(x, y):
				if a != b:
					raise Exception("Streams are not the same!")
				


	def get_file_info(self, file):
		cmd = ["mediainfo", file]
		p = None
		try:	
			p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
			return_code = p.wait()
			if return_code > 0:
				raise Exception("Could not retrieve file information")
		except SystemExit:
			try:
				p.kill()
			except Exception:
				pass
			raise SystemExit
		file_info = p.stdout.readlines()
		file_sections = re.findall('^Video.*?^\s*$|^Audio.*?^\s*$', "".join(file_info),re.M|re.S|re.I)	
		file_streams = list()
		for section in file_sections:
			video_section_re = re.search('.*^video.*', section, re.M|re.I|re.S)
			audio_section_re = re.search('.*^audio.*', section, re.M|re.I|re.S) 
			audio_number_re = re.search('.*?^audio #([0-9]*).*?$.*', section, re.M|re.I|re.S)
			video_number_re = re.search('.*?^video #([0-9]*).*?$.*', section, re.M|re.I|re.S)
			format_re = re.search('.*?^format.*?:(.*?)$.*', section, re.M|re.I|re.S)
			format_info_re = re.search('.*?^format/info.*?:(.*?)$.*', section, re.M|re.I|re.S)
			profile_re = re.search('.*?^format profile.*?:.*?@L(.*?)$.*', section, re.M|re.I|re.S)
			codec_id_re = re.search('.*?^codec id.*?:(.*?)$.*', section, re.M|re.I|re.S)
			width_re = re.search('.*?^width.*?:(.*?) pixels.*?$.*', section, re.M|re.I|re.S)
			height_re = re.search('.*?^height.*?:(.*?) pixels.*?$.*', section, re.M|re.I|re.S)
			aspect_ratio_re = re.search('.*?^display aspect ratio.*?:(.*?)$.*', section, re.M|re.I|re.S)
			fps_re = re.search('.*?^frame rate(?!.*?mode).*?:(.*?) fps.*?$.*', section, re.M|re.I|re.S)
			resolution_re = re.search('.*?^Resolution.*?:(.*?)$.*', section, re.M|re.I|re.S)
			encoding_settings_re = re.search('.*?^encoding settings.*?:(.*?)$.*', section, re.M|re.I|re.S)
			channels_re = re.search('.*?^channel\(s\).*?:(.*?) channels.*?$.*', section, re.M|re.I|re.S)
			stream = StreamInfo()	
			if video_section_re:
				stream.type = "video"
			elif audio_section_re:
				stream.type = "audio"
			else:
				continue


			if video_number_re:
				raise Exception("There cannot be more than one video stream")
			elif audio_number_re:
				stream.track = int(audio_number_re.group(1).strip())
			elif stream.type == "audio":
				stream.track = 1
			else:
				stream.track = 0

				
			
			if format_re:
				stream.format = format_re.group(1).strip()
			if format_info_re:
				stream.format_info = format_info_re.group(1).strip()
			if profile_re:
				stream.profile = profile_re.group(1).strip()
			if codec_id_re:
				stream.codec_id = codec_id_re.group(1).strip()
			if width_re:
				stream.width = width_re.group(1).strip()
			if height_re:
				stream.height = height_re.group(1).strip()
			if aspect_ratio_re:
				stream.aspect_ratio = aspect_ratio_re.group(1).strip()	
			if fps_re:
				stream.fps = fps_re.group(1).strip()
			if resolution_re:
				stream.resolution = resolution_re.group(1).strip()
			if encoding_settings_re:
				stream.encoding_settings = encoding_settings_re.group(1).strip()
			if channels_re:
				stream.channels = channels_re.group(1).strip()

			file_streams.append(stream)

		return file_streams	


class StreamInfo:
	#def __init__(self):
		#self.type = ""
		#self.format = ""
	#	self.track = 0
		#self.format_info = ""
		#self.profile = ""
		#self.codec_id = ""
		#self.width = ""
		#self.height = ""
		#self.aspect_ratio = ""
		#self.fps = ""
		#self.resolution = ""
		#self.encoding_settings = ""
		#self.channels = ""
		

	def __iter__(self):
		return self.__dict__.itervalues()

	def __len__(self):
		return len(self.__dict__.values())
	



class MKV2MP4:
	import subprocess
	import os
	import re
	def __init__(self, source, dest=None, fps="23.976", tmp="/tmp", audio="1", log=os.devnull, frames=0, seconds=0, channels=2):
		if os.path.isfile(source.strip()):
			self.source = source.strip()
		else:
			raise IOError('No such file ' + source)
		(sourcepath, sourcefile) = os.path.split(self.source)
		if dest:
			if os.path.isdir(os.path.dirname(dest)):
				if os.path.exists(dest):
					raise IOError(dest + ' already exists')
				else:
					self.dest = dest.strip()
			else:
				raise IOError(os.path.dirname(dest) + ' does not exist')
		else:
			dest = os.path.join(sourcepath, os.path.splitext(sourcefile)[0] + ".m4v")
			if os.path.exists(dest):
				raise IOError(dest + ' already exists')
			else:
				self.dest = dest
		self.frames = 0

		try:
			frames = int(frames)
			if frames > 0:
				self.frames = frames
		except Exception:
			pass
		try:
			seconds = int(seconds)
			if seconds > 0:
				self.seconds = seconds
		except Exception:
			self.seconds = 0

		self.audio = audio
		self.fps = fps
		self.crop = None
		if os.path.isdir(os.path.abspath(tmp)):
			self.tmp = os.path.abspath(tmp)
		else:
			os.makedirs(os.path.abspath(tmp))
			self.tmp = os.path.abspath(tmp)
		self.tmpfile = os.path.join(self.tmp, os.path.splitext(os.path.basename(self.dest))[0])	
		self.channels = str(int(float(channels.strip())))
		if int(self.channels) <= 2:
			self.multichannel = False
		else:
			self.multichannel = True
		self.delete = list()
		self.width = None
		self.height = None
		self.chap = None
		self.chapters = False
		if log == os.devnull:
			self.log = log
		elif log == None:
			self.log = os.devnull
		else:
			if os.path.isfile(os.path.abspath(log)):
				self.log = log
			else:
				self.log = os.devnull

	def start(self):
		tracks = list()
		#self.info()
		self.cropdetect()
		tracks.insert(0, self.convert_video())
		tracks.insert(2, self.convert_audio_aac())
		if self.multichannel:
			tracks.insert(1, self.convert_audio_ac3())
		if self.chapters:
			self.chap = self.get_chapters()
		self.mux(tracks)
		for path in self.delete:
			try:
				os.remove(path)
			except Exception:
				pass
		try:
			shutil.rmtree(self.tmp)
		except Exception:
			pass
		return self.dest

	def get_chapters(self):
		cmd = ['mkvextract', 'chapters', '-s', self.source]
		fd = open(self.log, 'a')
		output = open(self.tmpfile + '.txt', 'w')
		sys.stdout.write(' [+] Extracting chapter information...')
		sys.stdout.flush()
		ex = None
		try:
			ex = subprocess.Popen(cmd, stdout=output, stderr=fd) 
			s = Spinny()
			while ex.poll() is None:
				s.p()
			fd.close()
			output.close()
			s.finish('success')
			del s
		except SystemExit:
			try:
				ex.kill()
			except Exception:
				pass
			raise SystemExit
		if ex.poll() > 0:
			raise Exception('mkvextract crashed')
		chapterfile = self.tmpfile + '.txt'
		if not os.path.isfile(chapterfile):
			raise Exception(chapterfile + ' does not exist')
		self.delete.append(chapterfile)
		return chapterfile

	def mux(self, tracks):
		cmd = ['MP4Box']	
		for track in tracks:
			cmd.extend(['-add', track])
		if self.chapters:
			cmd.extend(['-chap', self.chap]) 
		cmd.extend(['-fps', self.fps])
		cmd.extend([self.dest])
		fd = open(self.log, 'a')
		sys.stdout.write(' [+] Muxing audio and video streams...')
		sys.stdout.flush()
		mp4box = None
		try:
			mp4box = subprocess.Popen(cmd, stdout=fd, stderr=fd)
			s = Spinny()
			while mp4box.poll() is None:
				s.p()
			s.finish('success')
			del s
			if mp4box.returncode > 0:
				raise Exception('MP4Box crashed')
		except SystemExit:
			try:
				mp4box.kill()
			except Exception:
				pass
			raise SystemExit
		if not os.path.isfile(self.dest):	
			raise Exception(self.dest + ' does not exist')	

		return self.dest
			

	def convert_audio_ac3(self):
		import fcntl
		import select
		import progressbar
		import time
		sys.stdout.write(' [+] Converting audio to ' + self.channels + ' dolby-digital sound\n')
		sys.stdout.flush()
		fd = open(self.log, 'a')
		aften = None
		ffmpeg = None
		pbar = None
		channels = 0
		try:
			channels = int(float(self.channels.strip()))
		except Exception:
			pass
		maxsize = int((self.seconds*768*channels)/8)
		if maxsize > 0:
			pbar = progressbar.ProgressBar(widgets=[progressbar.Bar('>'), progressbar.Percentage(), progressbar.ReverseBar('<'), progressbar.ETA()], maxval=int(maxsize + 100)).start()
		else:
			print " [-] No progress available because no time information was found"
		try:
			cmd = ['ffmpeg', '-i', self.source, '-vn', '-ac', str(int(round(float(self.channels)))), '-acodec', 'pcm_s16le', '-async', '1', '-f', 'wav', 'pipe:1']
			ffmpeg = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
			fcntl.fcntl(ffmpeg.stderr.fileno(), fcntl.F_SETFL, fcntl.fcntl(ffmpeg.stderr.fileno(), fcntl.F_GETFL) | os.O_NONBLOCK)	
			cmd = ['aften', '-b', '448', '-', self.tmpfile + '.ac3']
			aften = subprocess.Popen(cmd, stdin=ffmpeg.stdout, stdout=fd, stderr=fd) 
			size_re = re.compile('.*?size=.*?(\d+).*time.*')
			start = 0
			while ffmpeg.poll() is None:
				try:
					readx = select.select([ffmpeg.stderr.fileno()], [], [])[0]
					if readx:
						data = ffmpeg.stderr.read()	
						if data == '':
							continue
						if start < 11:
							start = start + 1
							time.sleep(1)
							continue
						size = size_re.search(data)
						if size:
							sizenum = int(size.group(1).strip())
							pbar.update(sizenum)
				except Exception, e:
					pass
				time.sleep(.1)

			return_aften = aften.wait()
			return_ffmpeg = ffmpeg.returncode

			if return_ffmpeg > 0:
				raise Exception('ffmpeg crashed')
			if return_aften >0:
				raise Exception('aften crashed')

		except SystemExit:
			raise SystemExit
		except Exception, e:
			raise e
		finally:
			try:
				aften.kill()
			except Exception:
				pass
			try:
				ffmpeg.kill()
			except Exception:
				pass
			fd.close()
			try:
				pbar.finish()
			except Exception:
				pass
				

		ac3file = self.tmpfile + '.ac3'

		if not os.path.isfile(ac3file):
			raise Exception(ac3file + ' failed to be created')
		self.delete.append(ac3file)

		return ac3file

	def convert_video(self):
		import fcntl
		import select
		import progressbar
		import time
 		cmd = ['ffmpeg', '-i', self.source, '-an', '-vsync', '0'] 
		vfsetting = list()
		if self.crop:
			vfsetting.append('crop=' + self.crop)
		if self.height is not None and self.width is not None and self.height > 990:
			new_height = None	
			for h in range(int(self.height.strip())-2, 0, -2):
				tmp = (float(self.width)*float(h))/float(self.height)
				tmp = round(tmp)
				tmp = int(tmp)
				div, mod = divmod(tmp, 2)
				if int(mod) == 0 and h < 990:
					new_height = h
					break
			vfsetting.append('scale=-1:' + str(new_height))
		if len(vfsetting) > 0:
			cmd.extend(['-vf', ",".join(vfsetting)])
		cmd.extend(['-vcodec', 'libx264', '-flags', '+loop', '-cmp', '+chroma', '-partitions', '+parti8x8+parti4x4+partp8x8+partb8x8', '-me_method', 'umh', '-coder', '1', '-subq', '8', '-me_range', '16', '-g', '250', '-keyint_min', '25', '-sc_threshold', '40', '-i_qfactor', '0.71', '-b_strategy', '2', '-qcomp', '0.6', '-qmin', '10', '-qmax', '51', '-qdiff', '4', '-bf', '3', '-refs', '5', '-directpred', '3', '-trellis', '1', '-flags2', '+bpyramid+mixed_refs+wpred+dct8x8+fastpskip', '-wpredp', '2', '-rc_lookahead', '50', '-crf', '22', '-level', '31', '-threads', '0', '-f', 'h264','-threads', '0', '-y', self.tmpfile + '.264'])
		fd = open(self.log, 'a')
		sys.stdout.write(' [+] Encoding the video from the mkv\n')
		sys.stdout.flush()
		ffmpeg = None
		pbar = progressbar.ProgressBar(widgets=[progressbar.Bar('>'), progressbar.Percentage(), ' Frame ', progressbar.SimpleProgress(), '  ', progressbar.ReverseBar('<'), progressbar.ETA()], maxval=int(self.frames + 100)).start()
		start = 0
		try:
			ffmpeg = subprocess.Popen(cmd,stdout=fd, stderr=subprocess.PIPE, close_fds=True)
			fcntl.fcntl(
			ffmpeg.stderr.fileno(), 
			fcntl.F_SETFL, 
			fcntl.fcntl(ffmpeg.stderr.fileno(), fcntl.F_GETFL) | os.O_NONBLOCK
			)	
			frame_re = re.compile('.*?frame=.*?(\d+).*fps.*')
			while ffmpeg.poll() is None:
				try:
					readx = select.select([ffmpeg.stderr.fileno()], [], [])[0]
					if readx:
						data = ffmpeg.stderr.read()	
						if data == '':
							continue
						if start < 11:
							start = start + 1
							time.sleep(.5)
							continue
						frames = frame_re.search(data.strip())
						if frames:
							framenum = int(frames.group(1).strip())
							pbar.update(framenum)
				except Exception, e:
					pass
				time.sleep(.1)
			return_ffmpeg = ffmpeg.returncode	
			if return_ffmpeg > 0:
				raise Exception('ffmpeg crashed')
		except SystemExit:
			raise SystemExit
		except Exception, e:
			raise e
		finally:
			try:
				ffmpeg.kill()
			except Exception:
				pass
			fd.close()
			pbar.finish()

		videofile = self.tmpfile + '.264'

		if not os.path.isfile(videofile):
			raise Exception(videofile + ' does not exist')

		self.delete.append(videofile)	
		return videofile

		
	def convert_audio_aac(self):
		import fcntl
		import select
		import progressbar
		import time
		cmd = ['ffmpeg', '-i', self.source, '-vn', '-ac', '2', '-acodec', 'pcm_s16le', '-async', '1', '-f', 'wav', 'pipe:1']
		fd = open(self.log, 'a')	
		ffmpeg = None
		nero = None
		mp4box = None
		sys.stdout.write(' [+] Converting audio to low-complexity AAC\n')	
		sys.stdout.flush()
		pbar = None
		channels = 2
		maxsize = int((self.seconds*768*channels)/8)
		if maxsize > 0:
			pbar = progressbar.ProgressBar(widgets=[progressbar.Bar('>'), progressbar.Percentage(), progressbar.ReverseBar('<'), progressbar.ETA()], maxval=int(maxsize + 100)).start()
		else:
			print " [-] No progress available because no time information was found"
		try:
			ffmpeg = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
			cmd = ['neroAacEnc', '-ignorelength', '-lc', '-q', '0.6', '-if', '-', '-of', self.tmpfile + '.m4a']	
			fcntl.fcntl(ffmpeg.stderr.fileno(), fcntl.F_SETFL, fcntl.fcntl(ffmpeg.stderr.fileno(), fcntl.F_GETFL) | os.O_NONBLOCK)	
			nero = subprocess.Popen(cmd, stdin=ffmpeg.stdout, stdout=fd, stderr=fd
			)
			start = 0
			size_re = re.compile('.*?size=.*?(\d+).*time.*')
			while ffmpeg.poll() is None:
				try:
					readx = select.select([ffmpeg.stderr.fileno()], [], [])[0]
					if readx:
						data = ffmpeg.stderr.read()	
						if data == '':
							continue
						if start < 11:
							start = start + 1
							time.sleep(.1)
							continue
						size = size_re.search(data)
						if size:
							sizenum = int(size.group(1).strip())
							pbar.update(sizenum)
				except Exception, e:
					pass
				time.sleep(.1)
			
			return_nero = nero.wait()
			return_ffmpeg = ffmpeg.returncode

			if return_ffmpeg > 0: 
				raise Exception('ffmpeg crashed decoding ' + self.source)
			if return_nero > 0:
				raise Exception('neroAacEnc crashed encoding ' + self.source)
		except SystemExit:
			raise SystemExit
		except Exception, e:
			raise e
		finally:
			try:
				ffmpeg.kill()
			except Exception:
				pass
			try:
				nero.kill()
			except Exception:
				pass
			fd.close()
			try:
				pbar.finish()
			except Exception:
				pass

		fd = open(self.log, 'a')	
		s = Spinny()
		try:
		
			sys.stdout.write(' [+] Extracting AAC from M4A container...')
			sys.stdout.flush()
			cmd = ['MP4Box', '-raw', '1', self.tmpfile + '.m4a'] 
			mp4box = subprocess.Popen(cmd, stdout=fd, stderr=fd)
			while mp4box.poll() is None:
				s.p()
			return_mp4box = mp4box.returncode 
			if return_mp4box > 0:
				raise Exception('MP4Box crashed')
			s.finish('success')	
		except SystemExit:
			s.finish('failed')	
			raise SystemExit
		except Exception, e:
			s.finish('failed')	
			raise e
		finally:
			try:
				mp4box.kill()
			except Exception:
				pass
			fd.close()

		if not os.path.isfile(self.tmpfile + "_track1.aac"):
			raise Exception('MP4Box failed')	

		os.rename(self.tmpfile + "_track1.aac", self.tmpfile + ".aac")
		aacfile = self.tmpfile + '.aac'
		try:
			os.remove(self.tmpfile + ".m4a")
		except Exception:
			pass
		self.delete.append(aacfile)
		return aacfile
		
		
		


	def info(self):
		cmd = ['ffmpeg', '-i', self.source]
		fd = open(self.log, 'a')
		p = None
		try:
			p = subprocess.Popen(cmd, stdout=fd, stderr=subprocess.PIPE)
			return_code = p.wait()
			fd.close()
			if return_code > 1:
				raise Exception("ffmpeg crashed")
		except SystemExit:
			try:
				p.kill()
			except Exception:
				pass
			raise SystemExit

		raw_lines = p.stderr.readlines()
		del p
		audio_info = re.findall('^\W*stream #0\.([0-9]*).*audio:(.*)$', "\n".join(raw_lines), re.I|re.M)
		#video_info = re.findall('^\W*stream #0\.([0-9]*).*video:(.*)$', "\n".join(raw_lines), re.I|re.M)
		chap_info = re.findall('^\W*chapter.*$', "\n".join(raw_lines), re.I|re.M)

		if len(audio_info) < 1:
			raise Exception('ffmpeg failed to output correct information')
		if len(chap_info) > 1:
			self.chapters = True

		for stream, info in audio_info:
			if stream.strip() != self.audio:
				continue
			sects = list(x.strip() for x in info.strip().split(","))
			channels = sects[2]
			channels = re.sub('[^\.0-9]*', '', channels)	
			if divmod(float(channels.strip()), 1)[1] >0:
				channels = divmod(float(channels.strip()), 1)[0] + 1
			else:
				channels = int(channels)
		
		if channels > 2:
			self.multichannel = True
			self.channels = str(channels)
			

	def cropdetect(self):
		total_loops = 10
		crop = list()
		sys.stdout.write(' [+] Finding crop settings...')
		sys.stdout.flush()
		s = Spinny()
		for x in range(1, total_loops):
			skip = x * 35
			#cmd = ['mplayer', self.source, '-ss', str(skip), '-identify', '-frames', '20', '-vo', 'md5sum', '-ao', 'null', '-nocache', '-vf', 'pp=lb,cropdetect=20:16'] 
			cmd = ['ffmpeg', '-ss', str(skip), '-vframes', '10', '-i', self.source, '-vf', 'cropdetect=20:16', '-sn', '-an', '-f', 'mp4', '-y', os.devnull]
			p = None
			try:
				p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
				
				while p.poll() is None:
					try:
						line = p.stderr.readline()
						if line is not None:
							crop_re = re.search('.*crop=(.*)$', line)
							if crop_re:
								crop.append(crop_re.group(1).strip())
								break
					except Exception:
						pass
						
					s.p()
				return_code = p.poll()
				if return_code > 0:
					raise Exception('ffmpeg crashed')
			except SystemExit:
				try:
					p.kill()
				except Exception:
					pass
				raise SystemExit

		s.finish('success')
		del s
		count_crop = list()
		for x in range(0, len(crop)):
			for y in range(0, len(crop)):
				if crop[x] == crop[y]:
					try:
						count_crop[x] = count_crop[x] + 1
					except Exception:
						count_crop.append(1)
		highest=0
		greatest=0
		for x in range(0, len(crop)):
			if count_crop[x] > highest:
				highest = count_crop[x]
				greatest = x
		try:
			self.crop = crop[greatest]
			sects = self.crop.split(":")
			self.width = sects[0]
			self.height = sects[1]
		except Exception:
			self.crop = None
			self.width = None
			self.height = None

		self.crop = crop[greatest].strip()

class Spinny:
	import time
	def __init__(self):
		self.bspace = 0
		self.current = 0
		self.spin = ['/', '-', '\\', '|']

	def p(self):
		import time
		for x in range(0, self.bspace):
			self.echo('\b')
			self.bspace = self.bspace -1

		self.echo(self.spin[self.current])		
		current = self.current + 1	
		self.bspace = self.bspace+1
		if current > 3:
			self.current = 0
		else:
			self.current = current
		time.sleep(.25)
		
	def echo(self, line):
		sys.stdout.write(line)
		sys.stdout.flush()
	def finish(self, line=''):
		for x in range(0, self.bspace):
			self.echo('\b')
		self.echo(line)
		self.echo('\n')



def signal_handler(sigint, frame):
	raise SystemExit

def exit(num=0):
	print "\n [-] Exiting..."
	try:
		os.wait()
	except Exception:
		pass

	sys.exit(num)

def parse_options():
	parser = optparse.OptionParser()
	parser.add_option('-o', '--output',  dest='output', action='store', type='string')
	(opts, args) = parser.parse_args()
	return (opts,args)


if __name__ == "__main__":
#	try:
	signal.signal(signal.SIGINT, signal_handler)
	main()
#	except Exception, e:
#		print "\n [-] " + str(e)
#		exit(1)	
			
#	except SystemExit:
#		exit(1)
#	else:
#		exit(1)
	

